Using Ruby Beneath a Java API

Who Am I?

DukeWithHelmet

Asciidoctor Open source implementation of AsciiDoc in Ruby

Ruby gem
Notes
  • "open source AsciiDoc renderer implemented in Ruby"
  • "which renders to html, docbook → pdf, ebook, or also custom templates (slim or haml)"
ballon
AsciiDoc is lightweight markup language
Notes
  • AsciiDoc is a lightweight markup language like Markdown, DocBook, Textile, MediaWiki but better because it is close to humans
  • "no worries about format, style or layout it is just plain text"

AsciiDoc

= Document Title
Doc Writer <doc@asciidoc.org>
v1.0, 2013-01-01: Initial version

http://asciidoc.org[AsciiDoc] is a lightweight markup language.

This is the optional preamble (an untitled section body), useful for
writing simple sectionless documents consisting only of a preamble.

NOTE: The abstract, preface, appendix, bibliography, glossary and
index section titles are significant (_specialsections_).

== First section

Document sections start at *level 1* and can nest four levels deep.

* Item 1
* Item 2

[source,ruby]
puts 'Hello, World!'

Rendered Document

render

Enables Collaboration

$ binary: git merge fix-village
warning: Cannot merge binary files: quijote.doc (HEAD vs. fix-village)

Auto-merging quijote.doc
CONFLICT (content): Merge conflict in quijote.doc
Automatic merge failed; fix conflicts and then commit the result.
$ binary: git diff master..fix-village
diff --git a/quijote.doc b/quijote.doc
index 4d7a47b..c8a6206 100644
Binary files a/quijote.doc and b/quijote.doc differ
live
Documentation is a living system

==!

  1. but it is in Ruby

Why we need in Java?

And for the rest of you

Ruby

Ruby is a dynamic, reflective, object-oriented, general-purpose programming language


Wikipedia
Ruby gem

Let’s see some examples

Classes

 require 'asciidoctor'
 class MyClass < Parent
    attr_accessor :foo

    def initialize
      @foo = 28
    end

    def has_more_lines?
    end

    def process_line line
    end

  end

  instance = MyClass.new
  puts instance.foo
  instance.foo = 496

Literals

age = 12 #Fixnum
debt = 123.45 #Float
msg = "Hello World" #String

array = ["a", "b", "c"] #Array
hash = {"a" => 1, "b"=>2} #Hash
hash2 = {:a => 1} #Hash Symbols

Modules

module Module1
  def self.myMethod
    puts "hi"
  end
  def others
  end
end

Module1.myMethod

JRuby

jruby

Java implementation of Ruby language

redbridge
RedBridge (Ruby Embedding)

Asciidoctor

Asciidoctor.render_file('mysample.adoc', :in_place => true,
    :backend => 'docbook5')
asciidoctor java
Asciidoctor Java integration

Eval Example

Ruby rubyRuntime = JavaEmbedUtils.initialize(new ArrayList());
RubyRuntimeAdapter evaler = JavaEmbedUtils.newRuntimeAdapter();

RubyInteger  rfj = evaler.eval(runtime, "1+2");
evaler.eval(runtime, "puts 'Hello World'");

Calling methods and autobinding

//Ruby block.rb
class Block < AbstractBlock
  def initialize(parent, context, opts = {})
  end
end

//Java
//Load Ruby script file
String scriptBlockClass = load("block.rb");
evaler.eval(runtime, scriptBlockClass);

//Load class
IRubyObject rubyClass = evaler.eval(runtime, "Block");

String context = ...;
Map<Object, Object> ops = ..;
Object[] parameters = new Object[]{ parent, context, ops };

//Parameters as Java types
JavaEmbedUtils.invokeMethod(runtime, rubyClass, "new", parameters, IRubyObject.class);

Calling methods and autobinding

x.somethingx.getSomething() x.something = 42x.setSomething(42) x.something?x.isSomething() x.method_namex.methodName()

Symbols

//Ruby

@block_extensions = {:block => class}
def find_block_extension name
      @block_extensions[name]
end

//Java

String blockName = "block";

this.asciidoctorModule.find_block_extension(
			RubySymbol.newSymbol(rubyRuntime, blockName));

Proxying Return Types

//Ruby
require 'java'
require 'asciidoctor'

class AsciidoctorModule	java_implements Java::AsciidoctorModuleClass
  def render_file(content, options = {})
    return Asciidoctor.render_file(content, options)
  end
  def render(content, options = {})
    return Asciidoctor.render(content, options)
  end
end

//Java
public interface AsciidoctorModuleClass {
  Object render(String content, Map<String, Object> options);
  Object render_file(String filename, Map<String, Object> options);
}
Object rfj = evaler.eval(runtime, "AsciidoctorModuleClass.new()");

AsciidoctorModuleClass amc = JavaEmbedUtils.rubyToJava(runtime, (org.jruby.runtime.builtin.IRubyObject) rfj, AsciidoctorModuleClass.class);

Proxying Return Types

javaproxy
ada
LazyInitializationException [joke]
duck
What is it?

Duck Typing

When I see a bird that walks like a duck and swims like a duck and quacks like a duck.


James Whitcomb
duck
Then I called a duck

Asciidoctor Extension API

A group of classes to modify content.

Example Of Extension

require 'asciidoctor'
require 'asciidoctor/extensions'
require 'uri-open'

class UriIncludeProcessor < Asciidoctor::Extensions::IncludeProcessor
  def handles? target
    target.start_with?('http://') or target.start_with?('https://')
  end

  def process reader, target, attributes
    content = open(target).readlines
    reader.push_include content, target, target, 1, attributes
  end
end

Asciidoctor::Extensions.register do |document|
  include_processor UriIncludeProcessor.new
end

Example Of Extension

public abstract class IncludeProcessor extends Processor {

    public IncludeProcessor() {
        this(new HashMap<String, Object>());
    }

    public IncludeProcessor(Map<String, Object> config) {
        super(config);
    }

    public abstract boolean handles(String target);
    public abstract void process(PreprocessorReader reader, String target, Map<String, Object> attributes);

}

extensionProcessor.includeProcessor(new IncludeProcessor(new HashMap<String, Object>()) {
	//----
}
gem
Standard format for distributing Ruby

Structure of Code

asciidoctor/
├── bin/
│   └── asciidoctor
├── lib/
│   └── asciidoctor/
│   └── asciidoctor.rb
├── data/
├── test/
│   └── attributes_test.rb
├── README.adoc
├── Gemfile
├── Rakefile
└── asciidoctor.gemspec

Classpath and Loadpath Unification

Embedding Asciidoctor

jar

TorqueBox

proxy

Maven Dependencies

<dependency>
	<groupId>rubygems</groupId>
	<artifactId>asciidoctor</artifactId>
	<version>${asciidoctor.version}</version>
	<type>gem</type>
	<scope>provided</scope>
</dependency>

<plugin>
	<groupId>de.saumya.mojo</groupId>
	<artifactId>gem-maven-plugin</artifactId>
	<version>1.0.0-rc4</version>
	<configuration>
	  <jrubyVersion>${jruby.version}</jrubyVersion>
	  <gemHome>${project.build.directory}/classes</gemHome>
	  <gemPath>${project.build.directory}/classes</gemPath>
 	</configuration>
	<!-- Executions configuration -->
</plugin>
wait
Continuous Integration becomes Continuous Waiting

Building Gem with Maven

wget -O asciidoctor-master.zip https://github.com/asciidoctor/asciidoctor/archive/master.zip
unzip asciidoctor-master.zip
cp install-asciidoctor-gem.pom asciidoctor-master/pom.xml
cd asciidoctor-master
ASCIIDOCTOR_VERSION=`grep 'VERSION' ./lib/asciidoctor/version.rb | sed "s/.*'\(.*\)'.*/\1/"`
sed -i "s;<version></version>;<version>$ASCIIDOCTOR_VERSION</version>;" pom.xml
mvn install
cd ..
rm -rf asciidoctor-master*
mvn test -Dasciidoctor.version=$ASCIIDOCTOR_VERSION

<!-- install-asciidoctor-gem.pom -->
<plugin>
	<groupId>de.saumya.mojo</groupId>
	<artifactId>gem-maven-plugin</artifactId>
	<version>${jruby.plugins.version}</version>
	<extensions>true</extensions>
</plugin>

Conclusions

Conclusions

cookie monster
github
github.com/asciidoctor/asciidoctorj
github
github.com/lordofthejars

Thanks!

Ruby is a dynamic, reflective, object-oriented, general-purpose programming language


Wikipedia